#ifdef ONIX
#include <onix/stdio.h>
#include <onix/string.h>
#include <onix/syscall.h>
#include <onix/fs.h>
#include "include-fix/stdlib.h"
#define O_BINARY 0
#else
#include <stdio.h>
//#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#endif

#include <mad.h>
#include "logger.h"
#include "pcm-player.h"

#ifndef VERSION
#define VERSION "0.0.1"
#endif

static inline signed int scale(mad_fixed_t sample) {
    /* round */
    sample += (1L << (MAD_F_FRACBITS - 16));

    /* clip */
    if (sample >= MAD_F_ONE)
        sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE)
        sample = -MAD_F_ONE;

    /* quantize */
    return sample >> (MAD_F_FRACBITS + 1 - 16);
}

static unsigned char input_buffer[8192];
static unsigned char output_buffer[8192 * 4];

int main(int argc, char *argv[]) {
    printf("Mini mp3-player Version: %s\n", VERSION_STR ", Built: "  __DATE__  " " __TIME__);
    if (argc < 2) {
        printf("Usage: %s <mp3-file>\n",  argv[0]);
        return 1;
    }

    int mp3_file = open(argv[1],  O_RDONLY  | O_BINARY, 0);
    if (!mp3_file) {
        printf("Unable to open file %s\n", argv[1]);
        return 1;
    }

    struct mad_stream stream;
    struct mad_frame frame;
    struct mad_synth synth;
    mad_timer_t timer;

    size_t input_size;
    int layer = 0;
    int samplerate = 44100;
    int channels = 2;

    // Initialize libmad
    mad_stream_init(&stream);
    mad_frame_init(&frame);
    mad_synth_init(&synth);

    int remain = 0;
    int read_size = 0;
    mad_timer_reset(&timer);
    while (1) {
        if(remain >0) {
            memmove(input_buffer, input_buffer + sizeof(input_buffer) - remain, remain);
        }
        read_size = read(mp3_file, input_buffer + remain , sizeof(input_buffer) - remain);
        if(read_size <= 0) {
            if(read_size == EOF) 
                ilog("file read EOF\n");
            else
                ilog("file read err :%d\n", read_size);
            break;
        }
        input_size = remain + read_size;
        if (input_size == 0) break; 
        mad_stream_buffer(&stream, input_buffer, input_size);
        while (1) {
            int RC = mad_frame_decode(&frame, &stream);
            dlog("MAD frame decode :%u , frame: %u , remain: %u\n", 
                stream.this_frame - stream.buffer , stream.next_frame - stream.this_frame , stream.bufend - stream.next_frame);

            remain = stream.bufend - stream.next_frame;
            if (RC != 0) {
                //dlog("MAD frame decode error:%s(%u)\n", mad_stream_errorstr(&stream), stream.error);
                if (MAD_RECOVERABLE(stream.error)) {
                    elog("Recoverable frame error\n");
                    continue;
                } else if (stream.error == MAD_ERROR_BUFLEN) {
                    break;
                } else {
                    elog("Unrecoverable frame error: %s\n", mad_stream_errorstr(&stream));
                    break;
                }
            }
            if(1)
            {
                int bits_per_sample = 16;                
                if(layer != frame.header.layer || samplerate != frame.header.samplerate) {
                    layer = frame.header.layer;
                    samplerate = frame.header.samplerate;
                    channels   = (frame.header.mode == MAD_MODE_SINGLE_CHANNEL ? 1 : 2);                    
                    ilog("===mp3 layer:%d channels:%d samplerate:%d===\n", frame.header.layer, channels, samplerate);
                    pcmPlayer_init(channels, samplerate, 16);
                    pcmPlayer_getParams(&channels, &samplerate, &bits_per_sample);
                    ilog("===snd hw channels:%d samplerate:%d===\n", channels, samplerate);
                }
            }

            mad_timer_add(&timer, frame.header.duration);
            int current_ms = mad_timer_count(timer, MAD_UNITS_MILLISECONDS);
            if(current_ms) {
                static int last_ms = 0;
                if(current_ms - last_ms >= 1000) {
                    ilog("time(ms):%d\n", current_ms);
                    last_ms = current_ms;
                }
            }
            
            mad_synth_frame(&synth, &frame);

            unsigned char *output_ptr = output_buffer;
            for (int i = 0; i < synth.pcm.length; i++) {
                signed int sample;

                sample = scale(synth.pcm.samples[0][i]);
                *(output_ptr++) = sample & 0xff;
                *(output_ptr++) = (sample >> 8) & 0xff;

                if (channels == 2) {
                    sample = scale(synth.pcm.samples[1][i]);
                    *(output_ptr++) = sample & 0xff;
                    *(output_ptr++) = (sample >> 8) & 0xff;
                }
            }
            pcmPlayer_write(output_buffer, (output_ptr - output_buffer));
        }
    }

    // Clean up
    mad_synth_finish(&synth);
    mad_frame_finish(&frame);
    mad_stream_finish(&stream);
    pcmPlayer_deinit();
    close(mp3_file);
    return 0;
}